#pragma once

#include <mutex>
#include <stack>
#include <memory>

struct empty_stack : std::exception
{
    const char *what() const throw()
    {
        return "empty stack!";
    }
};

template <class T>
class ThreadsafeStack
{
public:
    ThreadsafeStack(){}
    
    ThreadsafeStack(const ThreadsafeStack &ts)
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        _st = ts._st;// 保证数据安全的拷贝
    }

    ThreadsafeStack(ThreadsafeStack &&ts)
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        _st.swap(ts._st);
    }

    ThreadsafeStack &operator=(ThreadsafeStack ts)
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        _st.swap(ts._st);
    }

    void push(const T &data)
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        _st.push(data);
    }

    bool empty() const
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        return _st.empty();
    }

    void pop(T &data)
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        if(_st.empty()) throw empty_stack();

        data = _st.top();
        _st.pop();
    }

    std::shared_ptr<T> pop()
    {
        std::unique_lock<std::mutex> _lock(_mutex);
        if(_st.empty()) throw empty_stack();
        
        std::shared_ptr<T> res(std::make_shared<T>(_st.top()));
        _st.pop();
        return res;
    }
private:
    std::mutex _mutex;// 互斥元
    std::stack<T> _st;// 栈
};